<?php


/**
 * 邮件传输协议操作.
 *
 * @author Qian Su <aoxue.1988.su.qian@163.com>
 * @copyright ©2003-2103 phpwind.com
 * @license http://www.windframework.com
 *
 * @version $Id: WindSmtp.php 3904 2013-01-08 07:01:26Z yishuo $
 */
class WindSmtp
{
    const CRLF = "\r\n";

    /**
     * @var WindSocket
     */
    protected $smtp = null;

    protected $request = array();

    protected $resonse = array();

    public function __construct($host, $port, $timeout = 60)
    {
        $this->smtp = new WindSocket($host, $port, $timeout);
    }

    /**
     * 打开smtp服务器,建立连接.
     *
     * @return string
     */
    public function open()
    {
        $this->smtp->open();

        return $this->checkResponse(220);
    }

    /**
     * 向服务器标识用户身份.
     *
     * @param string $host 身份
     *
     * @return string
     */
    public function ehlo($host)
    {
        $this->request('EHLO '.$host);

        return $this->checkResponse(250);
    }

    /**
     * 进行用户身份认证
     *
     * @param string $username 用户名
     * @param string $password 密码
     *
     * @return string
     */
    public function authLogin($username, $password)
    {
        $this->request('AUTH LOGIN');
        $this->checkResponse(array(334));
        $this->request(base64_encode($username));
        $this->checkResponse(array(334));
        $this->request(base64_encode($password));

        return $this->checkResponse(array(235));
    }

    /**
     * 指定的地址是发件人地址
     *
     * @param string $from 邮件发送者
     *
     * @return string
     */
    public function mailFrom($from)
    {
        $this->request('MAIL FROM:'.'<'.$from.'>');

        return $this->checkResponse(250);
    }

    /**
     * 指定的地址是收件人地址
     *
     * @param string $to 邮件发送者
     *
     * @return string
     */
    public function rcptTo($to)
    {
        $this->request('RCPT TO:'.'<'.$to.'>');

        return $this->checkResponse(array(250, 251));
    }

    /**
     * 用于验证指定的用户/邮箱是否存在；由于安全方面的原因，服务器常禁止此命令.
     *
     * @param string $user
     *
     * @return string
     */
    public function very($user)
    {
        $this->request('VRFY '.$user);

        return $this->checkResponse(array(250, 251, 252));
    }

    /**
     * 验证给定的邮箱列表是否存在，扩充邮箱列表，也常被禁用.
     *
     * @param string $name
     *
     * @return string
     */
    public function expn($name)
    {
        $this->request('EXPN '.$name);
        $response = $this->checkResponse(250);
        $entries = explode(self::CRLF, $response);
        while (list(, $l) = each($entries)) {
            $list[] = substr($l, 4);
        }

        return $list;
    }

    /**
     * 无操作，服务器应响应 OK.
     *
     * @return string
     */
    public function noop()
    {
        $this->request('NOOP');

        return $this->checkResponse(250);
    }

    /**
     * 在单个或多个 RCPT 命令后，表示所有的邮件接收人已标识，并初始化数据传输，以 CRLF.CRLF 结束
     *
     * @param string $data 发送的数据
     *
     * @return string
     */
    public function data($data)
    {
        $this->request('DATA');
        $this->checkResponse(354);
        $data = str_replace("\r\n", "\n", $data);
        $data = str_replace("\r", "\n", $data);
        $lines = explode("\n", $data);
        foreach ($lines as $line) {
            if (0 === strpos($line, '.')) {
                $line = '.'.$line;
            }
            $this->request($line);
        }
        $this->request('.');

        return $this->checkResponse(250);
    }

    /**
     * 重置会话，当前传输被取消.
     *
     * @return string
     */
    public function rset()
    {
        $this->request('RSET');

        return $this->checkResponse(array(250, 220));
    }

    /**
     * 结束会话.
     *
     * @return string
     */
    public function quit()
    {
        $this->request('QUIT');

        return $this->checkResponse(221);
    }

    /**
     * 关闭smtp服务器.
     */
    public function close()
    {
        $this->smtp->close();
        $this->smtp = null;
    }

    /**
     * smtp响应请求
     *
     * @param int $timeout
     */
    public function responseLine($timeout = null)
    {
        if (null !== $timeout) {
            $this->smtp->setSocketTimeOut((int) $timeout);
        }

        return $this->smtp->responseLine();
    }

    /**
     * 发送smtp命令.
     *
     * @param string $request
     */
    public function request($request)
    {
        $this->request[] = $request.self::CRLF;

        return $this->smtp->request($request.self::CRLF);
    }

    /**
     * 验证请求
     *
     * @param string $expect
     * @param int    $timeout
     *
     * @return string
     */
    public function checkResponse($expect, $timeout = null)
    {
        $response = '';
        $expect = is_array($expect) ? $expect : array($expect);
        while ('' != ($_response = $this->responseLine($timeout))) {
            $response .= $_response;
            $this->resonse[] = $_response;
            list($code, $info) = preg_split('/([\s-]+)/', $_response, 2);
            if (null === $code || !in_array($code, $expect)) {
                throw new WindException('[mail.protocol.WindSmtp.checkResponse] '.$info);
            }
            if (' ' == substr($_response, 3, 1)) {
                break;
            }
        }
        if (empty($response)) {
            throw new WindException('[mail.protocol.WindSmtp.checkResponse] No response');
        }

        return $response;
    }

    public function __destruct()
    {
        if ($this->smtp) {
            $this->close();
        }
    }
}
